﻿using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Linq;
using System.IO;
using System.Linq;
using System.Text;

namespace DocumentServices.Storage.Database
{
    /// <summary>
    /// Provides a reference implementation for documents stored in a database.
    /// </summary>
    /// <remarks>
    /// <para>
    /// The DbDocumentStore uses the Document LinqToSql context as defined in the Database folder.
    /// </para>
    /// </remarks>
    public class DbDocumentStore
        : DocumentStoreBase
    {
        private DocumentsDataContext context;

        /// <summary>
        /// Initializes a new instance of the <see cref="DbDocumentStore"/> class.
        /// </summary>
        /// <param name="context">The context.</param>
        public DbDocumentStore(DocumentsDataContext context)
        {
            if (context == null) {
                throw new ArgumentNullException("context");
            }
            this.context = context;
        }

        /// <summary>
        /// Adds specified document to the store.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="document">The document.</param>
        public override void AddDocument(string name, Stream document)
        {
            var documentInfo = ReadDocumentInfo(name, document);
            var data = new byte[document.Length];
            document.Read(data, 0, data.Length);

            this.context.Documents.InsertOnSubmit(new Document() {
                Author = documentInfo.Author,
                Data = new Binary(data),
                Name = name,
                LastModified = DateTime.Now,
                PageCount = documentInfo.PageCount,
                Thumbnail = documentInfo.ThumbnailUri.OriginalString,
                Title = documentInfo.Title
            });

            this.context.SubmitChanges();
        }

        /// <summary>
        /// Determines whether the specified document exists.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns>
        /// 	<c>true</c> if the specified document exists; otherwise, <c>false</c>.
        /// </returns>
        public override bool ContainsDocument(string name)
        {
            return this.context.Documents.Any(d => d.Name == name);
        }

        /// <summary>
        /// Gets the document's last modification date.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public override DateTime GetDocumentLastModified(string name)
        {
            return (from document in this.context.Documents
                    where document.Name == name
                    select document.LastModified).First();
        }

        /// <summary>
        /// Gets all documents contained in this store.
        /// </summary>
        /// <returns></returns>
        public override IEnumerable<DocumentInfo> GetDocuments()
        {
            return from document in this.context.Documents
                   select new DocumentInfo(document.Name) {
                       Author = document.Author,
                       PageCount = document.PageCount,
                       ThumbnailUri = new Uri(document.Thumbnail, UriKind.Relative),
                       Title = document.Title
                   };
        }

        /// <summary>
        /// Opens the specified document for reading.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public override Stream OpenDocument(string name)
        {
            return new MemoryStream(
                (from document in this.context.Documents
                 where document.Name == name
                 select document.Data.ToArray()).First());
        }

        /// <summary>
        /// Removes specified document from the store.
        /// </summary>
        /// <param name="name">The name.</param>
        public override void RemoveDocument(string name)
        {
            this.context.Documents.DeleteOnSubmit((
                from document in this.context.Documents
                where document.Name == name
                select document).First());
            this.context.SubmitChanges();
        }
    }
}
